Skip to content

feat: migrate OutboxStore from Exposed to plain JDBC (KOJAK-64)#26

Open
endrju19 wants to merge 6 commits intomainfrom
feat/jdbc-outbox-store
Open

feat: migrate OutboxStore from Exposed to plain JDBC (KOJAK-64)#26
endrju19 wants to merge 6 commits intomainfrom
feat/jdbc-outbox-store

Conversation

@endrju19
Copy link
Copy Markdown
Collaborator

Summary

  • Replaces Exposed ORM in PostgresOutboxStore and MysqlOutboxStore with plain JDBC via new ConnectionProvider fun interface
  • Adds SpringConnectionProvider (uses DataSourceUtils.getConnection()) for seamless Spring integration with any PlatformTransactionManager (JPA, JDBC, jOOQ, MyBatis)
  • Removes OutboxTable Exposed table definitions from both postgres and mysql modules
  • Removes Exposed dependencies from okapi-postgres, okapi-mysql, and okapi-spring-boot

Motivation

Exposed as an internal implementation detail of OutboxStore forced consumers to:

  1. Add 5+ Exposed runtime dependencies (compileOnly in okapi leaked at runtime)
  2. Use Exposed's SpringTransactionManager instead of standard Spring TMs
  3. Create hacks like JpaFlushingTransactionManager for JPA coexistence

With ConnectionProvider, okapi borrows the JDBC connection from whatever transaction mechanism the app uses — zero coupling.

Key changes

Module Change
okapi-core New ConnectionProvider fun interface
okapi-postgres PostgresOutboxStore(connectionProvider, clock) — pure JDBC
okapi-mysql MysqlOutboxStore(connectionProvider, clock) — pure JDBC
okapi-spring-boot SpringConnectionProvider(dataSource) auto-configured
okapi-spring-boot OutboxPurger now takes optional TransactionRunner
okapi-integration-tests JdbcConnectionProvider test helper, updated all tests

Validated by

5 standalone example apps (JPA, JDBC, multi-datasource, jOOQ, Ktor+Exposed) — all passing JUnit tests and smoke tests against this branch.

Test plan

  • All existing integration tests pass (./gradlew test)
  • Concurrency tests pass (PostgreSQL + MySQL)
  • E2E tests pass (HTTP + Kafka delivery)
  • CI green

Replace Exposed ORM with plain JDBC in PostgresOutboxStore and
MysqlOutboxStore. This eliminates the coupling to Exposed's transaction
mechanism, allowing okapi to work transparently with any Spring
PlatformTransactionManager — JPA/Hibernate, JDBC, jOOQ, MyBatis, etc.

Key changes:
- Add ConnectionProvider interface to okapi-core
- Add SpringConnectionProvider using DataSourceUtils.getConnection()
- Rewrite PostgresOutboxStore and MysqlOutboxStore to plain JDBC
- Remove Exposed dependencies from okapi-postgres and okapi-mysql
- Delete OutboxTable.kt from both modules
- Add TransactionRunner to OutboxPurger (fixes connection leak)
- Fix null parameter handling (setNull instead of setTimestamp(null))
- Parameterize status in removeDeliveredBefore SQL
- Change spring-jdbc from compileOnly to implementation
Move ExposedTransactionContextValidator out of okapi-core into a
dedicated okapi-exposed module. Add ExposedConnectionProvider and
ExposedTransactionRunner for Ktor/standalone Exposed apps.

okapi-core is now free of Exposed dependencies — only plain JDK types.
* JPA, JDBC, jOOQ, MyBatis, Exposed, etc.
*/
class SpringConnectionProvider(private val dataSource: DataSource) : ConnectionProvider {
override fun getConnection(): Connection = DataSourceUtils.getConnection(dataSource)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible conection leak when called without an active Spring transaction>

DataSourceUtils.getConnection(ds) returns a transaction-bound connection when one exists, but otherwise fetches a fresh connection from the pool that the caller is contractually required to release via DataSourceUtils.releaseConnection(conn, ds)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants